package com.meiyuetao.myt.c2c.service;

import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import lab.s2jh.core.dao.BaseDao;
import lab.s2jh.core.pagination.GroupPropertyFilter;
import lab.s2jh.core.pagination.PropertyFilter;
import lab.s2jh.core.pagination.PropertyFilter.MatchType;
import lab.s2jh.core.service.BaseService;
import lab.s2jh.core.util.DateUtils;

import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.meiyuetao.myt.c2c.dao.C2cRunningAccountDao;
import com.meiyuetao.myt.c2c.entity.C2cFinanceAccount;
import com.meiyuetao.myt.c2c.entity.C2cFinanceAccount.AccountTypeCodeEnum;
import com.meiyuetao.myt.c2c.entity.C2cRunningAccount;
import com.meiyuetao.myt.c2c.entity.C2cRunningAccount.BizNumTypeEnum;
import com.meiyuetao.myt.c2c.entity.C2cSettle;
import com.meiyuetao.myt.c2c.entity.C2cSettle.SettleTypeEnum;
import com.meiyuetao.myt.c2c.entity.C2cShopInfo;
import com.meiyuetao.myt.customer.entity.CustomerProfile;
import com.meiyuetao.myt.sale.entity.BoxOrder;
import com.meiyuetao.myt.sale.entity.BoxOrder.BoxOrderStatusEnum;
import com.meiyuetao.myt.sale.service.BoxOrderService;

@Service
@Transactional
public class C2cRunningAccountService extends BaseService<C2cRunningAccount, Long> {

    @Autowired
    private C2cRunningAccountDao c2cRunningAccountDao;

    @Override
    protected BaseDao<C2cRunningAccount, Long> getEntityDao() {
        return c2cRunningAccountDao;
    }

    @Autowired
    private C2cFinanceAccountService accountService;

    @Autowired
    private C2cSettleService settleService;

    @Autowired
    private C2cShopInfoService shopInfoService;

    @Autowired
    private BoxOrderService orderService;

    @Autowired
    private JdbcTemplate jdbcTemplate;

    public void closeSubside() {
        Calendar c = Calendar.getInstance();
        c.add(Calendar.MONTH, -1);
        c.set(Calendar.DAY_OF_MONTH, c.getActualMaximum(Calendar.DAY_OF_MONTH));
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
        String lastMonthEnd = dateFormat.format(c.getTime());
        String lastMonthBegin = lastMonthEnd.substring(0, 7).concat("-01");

        GroupPropertyFilter groupPropertyFilter = GroupPropertyFilter.buildDefaultAndGroupFilter();
        // 未结算
        groupPropertyFilter.append(new PropertyFilter(MatchType.EQ, "isSettled", false));
        // 已解冻
        groupPropertyFilter.append(new PropertyFilter(MatchType.EQ, "isFrozen", false));
        // 解冻时间为前一个月内
        groupPropertyFilter.append(new PropertyFilter(MatchType.GE, "unfreezeTime", DateUtils.parseDate(lastMonthBegin)));
        groupPropertyFilter.append(new PropertyFilter(MatchType.LT, "unfreezeTime", DateUtils.parseDate(lastMonthEnd)));
        List<C2cRunningAccount> runningAccounts = super.findByFilters(groupPropertyFilter);
        if (CollectionUtils.isNotEmpty(runningAccounts)) {
            Date now = DateUtils.parseDate(DateUtils.formatDateNow());
            String yearMonDay = dateFormat.format(now);
            SimpleDateFormat ymFormat = new SimpleDateFormat("yyyy-MM");
            String yearMon = ymFormat.format(now);
            Map<C2cFinanceAccount, Map<String, Integer>> recMap = new HashMap<C2cFinanceAccount, Map<String, Integer>>();
            Map<C2cFinanceAccount, Map<CustomerProfile, Integer>> recCus = new HashMap<C2cFinanceAccount, Map<CustomerProfile, Integer>>();
            Map<C2cFinanceAccount, BigDecimal> maxMap = new HashMap<C2cFinanceAccount, BigDecimal>();
            Map<C2cFinanceAccount, BigDecimal> btTotalAmount = new HashMap<C2cFinanceAccount, BigDecimal>();
            for (C2cRunningAccount runningAccount : runningAccounts) {
                // 补贴账户
                C2cFinanceAccount btaccount = runningAccount.getC2cFinanceAccount();
                if (AccountTypeCodeEnum.B01.equals(btaccount.getAccountTypeCode()) && BizNumTypeEnum.ORDER.equals(runningAccount.getBizNumType())) {
                    if (!btTotalAmount.containsKey(btaccount)) {
                        btTotalAmount.put(btaccount, runningAccount.getAmount());
                    } else {
                        btTotalAmount.put(btaccount, btTotalAmount.get(btaccount).add(runningAccount.getAmount()));
                    }
                    // 每个店铺的买家、收货人，每月最多补贴3笔订单。
                    BoxOrder order = orderService.findByProperty("orderSeq", runningAccount.getBizNum());
                    if (!recMap.containsKey(btaccount)) {
                        Map<String, Integer> phoneMap = new HashMap<String, Integer>();
                        phoneMap.put(order.getMobilePhone(), 1);
                        recMap.put(btaccount, phoneMap);
                    } else {
                        if (!recMap.get(btaccount).containsKey(order.getMobilePhone())) {
                            recMap.get(btaccount).put(order.getMobilePhone(), 1);
                        } else {
                            recMap.get(btaccount).put(order.getMobilePhone(), recMap.get(btaccount).get(order.getMobilePhone()) + 1);
                            if (recMap.get(btaccount).get(order.getMobilePhone()) > 3) {
                                continue;
                            }
                        }
                    }
                    // 每个店铺的买家、收货人，每月最多补贴3笔订单。
                    if (!recCus.containsKey(btaccount)) {
                        Map<CustomerProfile, Integer> cusMap = new HashMap<CustomerProfile, Integer>();
                        cusMap.put(order.getCustomerProfile(), 1);
                        recCus.put(btaccount, cusMap);
                    } else {
                        if (!recCus.get(btaccount).containsKey(order.getCustomerProfile())) {
                            recCus.get(btaccount).put(order.getCustomerProfile(), 1);
                        } else {
                            recCus.get(btaccount).put(order.getCustomerProfile(), recCus.get(btaccount).get(order.getCustomerProfile()) + 1);
                            if (recCus.get(btaccount).get(order.getCustomerProfile()) > 3) {
                                continue;
                            }
                        }
                    }

                    // 初始保证金1000元，对应补贴封顶额度2万元。如果无保证金，则封顶补贴额度为5000元
                    BigDecimal top = btaccount.getShop().getSubsidyCap();
                    if (maxMap.containsKey(btaccount)) {
                        maxMap.put(btaccount, maxMap.get(btaccount).add(runningAccount.getAmount()));
                        if (maxMap.get(btaccount).compareTo(top) >= 0) {
                            // 若即将达到封顶，则补满到封顶，多余的不再补贴
                            maxMap.put(btaccount, top);
                            continue;
                        }
                    } else {
                        maxMap.put(btaccount, runningAccount.getAmount());
                    }
                }
            }

            for (Map.Entry<C2cFinanceAccount, BigDecimal> totalEntity : btTotalAmount.entrySet()) {
                for (Map.Entry<C2cFinanceAccount, BigDecimal> btEntity : maxMap.entrySet()) {
                    if (totalEntity.getKey().equals(btEntity.getKey())) {
                        C2cFinanceAccount mainAccount = btEntity.getKey().getParent();
                        // 将补贴核算到主账户余额账户中
                        GroupPropertyFilter mainLeftPf = GroupPropertyFilter.buildDefaultAndGroupFilter();
                        mainLeftPf.append(new PropertyFilter(MatchType.EQ, "parent", mainAccount));
                        mainLeftPf.append(new PropertyFilter(MatchType.EQ, "accountTypeCode", AccountTypeCodeEnum.B00));
                        List<C2cFinanceAccount> mainLeftAccounts = accountService.findByFilters(mainLeftPf);
                        C2cFinanceAccount mainLeftAccount = mainLeftAccounts.get(0);
                        mainLeftAccount.setAmount(mainLeftAccount.getAmount().add(btEntity.getValue()));
                        accountService.save(mainLeftAccount);

                        // 补贴账户对应减去 转到主账户余额账户的 补贴金
                        btEntity.getKey().setAmount(btEntity.getKey().getAmount().subtract(btEntity.getValue()));
                        accountService.save(btEntity.getKey());

                        // 将补贴记录到 结算表
                        GroupPropertyFilter settlePf = GroupPropertyFilter.buildDefaultAndGroupFilter();
                        settlePf.append(new PropertyFilter(MatchType.EQ, "financeAccount", mainAccount));
                        List<C2cSettle> c2cSettles = settleService.findByFilters(settlePf);
                        C2cSettle c2cSettle = new C2cSettle();
                        if (CollectionUtils.isNotEmpty(c2cSettles)) {
                            c2cSettle = c2cSettles.get(0);
                            c2cSettle.setBeforeSettleAmount(c2cSettle.getAfterSettleAmount());
                        } else {
                            c2cSettle.setBeforeSettleAmount(BigDecimal.ZERO);
                            c2cSettle.setFinanceAccount(mainAccount);
                        }
                        c2cSettle.setSettleAmount(btEntity.getValue());
                        c2cSettle.setAfterSettleAmount(c2cSettle.getBeforeSettleAmount().add(c2cSettle.getSettleAmount()));
                        c2cSettle.setSettleTime(now);
                        c2cSettle.setSettleType(SettleTypeEnum.BTJS);
                        settleService.save(c2cSettle);
                        // 记录主账户余额账户入账的流水
                        C2cRunningAccount mainLeftHis = new C2cRunningAccount();
                        mainLeftHis.setOccureTime(now);
                        mainLeftHis.setOccureYearMon(yearMon);
                        mainLeftHis.setOccureDay(yearMonDay);
                        mainLeftHis.setC2cFinanceAccount(mainLeftAccount);
                        mainLeftHis.setIsSettled(true);
                        mainLeftHis.setIsFrozen(false);
                        mainLeftHis.setBizNumType(BizNumTypeEnum.SETTLE);
                        mainLeftHis.setBizNum(c2cSettle.getId().toString());
                        mainLeftHis.setAmount(btEntity.getValue());
                        mainLeftHis.setBizMemo("主账户余额账户上个月入账：" + btEntity.getValue());
                        super.save(mainLeftHis);

                        // 记录补贴账户出账的流水
                        C2cRunningAccount btHis = new C2cRunningAccount();
                        btHis.setOccureTime(now);
                        btHis.setOccureYearMon(yearMon);
                        btHis.setOccureDay(yearMonDay);
                        btHis.setC2cFinanceAccount(btEntity.getKey());
                        btHis.setIsSettled(true);
                        btHis.setIsFrozen(false);
                        btHis.setBizNumType(BizNumTypeEnum.SETTLE);
                        btHis.setBizNum(c2cSettle.getId().toString());
                        btHis.setAmount(BigDecimal.ZERO.subtract(btEntity.getValue()));
                        btHis.setBizMemo("补贴账户上个月出账：" + btEntity.getValue());
                        super.save(btHis);
                        if (totalEntity.getValue().compareTo(btEntity.getValue()) > 0) {
                            BigDecimal errBt = totalEntity.getValue().subtract(btEntity.getValue());
                            // 主账户和补贴账户需要将不合理补贴进行清除，并做记录
                            mainAccount.setAmount(mainAccount.getAmount().subtract(errBt));
                            accountService.save(mainAccount);
                            // 记录主账户清理不合理补贴
                            C2cRunningAccount parentHis = new C2cRunningAccount();
                            parentHis.setOccureTime(now);
                            parentHis.setOccureYearMon(yearMon);
                            parentHis.setOccureDay(yearMonDay);
                            parentHis.setC2cFinanceAccount(mainLeftAccount);
                            parentHis.setIsSettled(true);
                            parentHis.setIsFrozen(false);
                            parentHis.setBizNumType(BizNumTypeEnum.ERR);
                            parentHis.setBizNum(c2cSettle.getId().toString());
                            parentHis.setAmount(BigDecimal.ZERO.subtract(errBt));
                            parentHis.setBizMemo("主账户扣除不合理补贴：" + errBt);
                            super.save(parentHis);

                            // 主账户和补贴账户需要将不合理补贴进行清除，并做记录
                            btEntity.getKey().setAmount(btEntity.getKey().getAmount().subtract(errBt));
                            accountService.save(btEntity.getKey());
                            // 记录补贴账户不合理补贴
                            C2cRunningAccount btErrHis = new C2cRunningAccount();
                            btErrHis.setOccureTime(now);
                            btErrHis.setOccureYearMon(yearMon);
                            btErrHis.setOccureDay(yearMonDay);
                            btErrHis.setC2cFinanceAccount(btEntity.getKey());
                            btErrHis.setIsSettled(true);
                            btErrHis.setIsFrozen(false);
                            btErrHis.setBizNumType(BizNumTypeEnum.ERR);
                            btErrHis.setBizNum(c2cSettle.getId().toString());
                            btErrHis.setAmount(BigDecimal.ZERO.subtract(errBt));
                            btErrHis.setBizMemo("补贴账户扣除不合理补贴：" + errBt);
                            super.save(btErrHis);
                        }
                        // 将补贴核算到店铺信息 补贴额中
                        C2cShopInfo shop = mainAccount.getShop();
                        shop.setSubsidy(shop.getSubsidy().add(btEntity.getValue()));
                        shopInfoService.save(shop);
                    }
                }
            }

            // 设置流水结算标识为 已结算
            for (C2cRunningAccount runningAccount : runningAccounts) {
                runningAccount.setIsSettled(true);
                super.save(runningAccount);
            }
        }
    }

    /**
     * 每日定时任务，将前一天的冻结金额解冻
     */
    public void unfreeze() {
        Calendar c = Calendar.getInstance();
        c.set(Calendar.HOUR_OF_DAY, 0);
        c.set(Calendar.MINUTE, 0);
        c.set(Calendar.SECOND, 0);
        GroupPropertyFilter boProFlt = GroupPropertyFilter.buildDefaultAndGroupFilter();
        boProFlt.append(new PropertyFilter(MatchType.LT, "actualSuccessTime", c.getTime()));
        c.add(Calendar.DAY_OF_YEAR, -1);
        boProFlt.append(new PropertyFilter(MatchType.GE, "actualSuccessTime", c.getTime()));
        boProFlt.append(new PropertyFilter(MatchType.EQ, "orderStatus", BoxOrderStatusEnum.S70CLS));
        List<BoxOrder> boxOrders = orderService.findByFilters(boProFlt);
        if (CollectionUtils.isNotEmpty(boxOrders)) {
            Calendar cnow = Calendar.getInstance();
            Date now = cnow.getTime();
            List<String> orderSqlList = new ArrayList<String>();
            for (BoxOrder boxOrder : boxOrders) {
                orderSqlList.add(boxOrder.getOrderSeq());
            }
            GroupPropertyFilter fahProFlt = GroupPropertyFilter.buildDefaultAndGroupFilter();
            fahProFlt.append(new PropertyFilter(MatchType.EQ, "bizNumType", BizNumTypeEnum.ORDER));
            fahProFlt.append(new PropertyFilter(MatchType.IN, "bizNum", orderSqlList));
            fahProFlt.append(new PropertyFilter(MatchType.EQ, "isFrozen", true));
            List<C2cRunningAccount> runningAccounts = super.findByFilters(fahProFlt);
            if (CollectionUtils.isNotEmpty(runningAccounts)) {
                for (C2cRunningAccount runningAccount : runningAccounts) {
                    runningAccount.setUnfreezeTime(now);
                    runningAccount.setIsFrozen(false);
                    C2cFinanceAccount financeAccount = runningAccount.getC2cFinanceAccount();
                    financeAccount.setFrozenAmount(financeAccount.getFrozenAmount().subtract(runningAccount.getAmount()));
                    accountService.save(financeAccount);
                    super.save(runningAccount);
                }
            }
        }
    }
}
